#ifndef __VECTOR3D_H__
#define __VECTOR3D_H__

////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include "Point3D.hpp"
#include "DivisionByZeroException.hpp"
#include <cmath>

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace Types {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

/// Vector 3D type
template <typename T>
class Vector3D : public Point3D<T> {

	public:
	
		/// Ccnstructor
		Vector3D() {}

		/// Constructor with initialisation values
		Vector3D(const T & x, const T & y, const T & z) : Point3D<T>(x,y,z) {}

		/// Create a copy of the given instance
		template <typename T2>
		Vector3D(const Vector3D<T2> & inst)
		: Point3D<T>(inst) {}

		/// Affectation operator
		template <typename T2>
		Vector3D<T> & operator = (const Vector3D<T2> & inst) {
			if (this != &inst) {
				this->x	= static_cast<T>(inst.x);
				this->y = static_cast<T>(inst.y);
				this->z = static_cast<T>(inst.z);
			}
			return *this;
		}

		/// Return the norm of the vector
		const double Norm() const {
			return std::sqrt(this->x*this->x + this->y*this->y + this->z*this->z);
		}
		
		/// Return the squared norm of the vector
		const double SquaredNorm() const {
			return this->x*this->x + this->y*this->y + this->z*this->z;
		}

		/// Normalise the vector
		void Normalise() {
			double norm = this->Norm();
			this->x =  static_cast<T>(((double)this->x)/norm);
			this->y =  static_cast<T>(((double)this->y)/norm);
			this->z =  static_cast<T>(((double)this->z)/norm);
		}
		
		/// Return the normalised vector
		const Vector3D<T> GetNormalised() const {
			const double norm = this->Norm();
			return Vector3D<T>	(	 static_cast<T>(((double)this->x)/norm),
									 static_cast<T>(((double)this->y)/norm),
									 static_cast<T>(((double)this->z)/norm)
								);
		}

		/// Add two vectors
		const Vector3D<T> operator+ (const Vector3D<T> & v) const {
			return Vector3D<T> ( this->x + v.x, this->y + v.y, this->z + v.z);
		}

		/// Subtract two vectors
		const Vector3D<T> operator- (const Vector3D<T> & v) const {
			return Vector3D<T> ( this->x - v.x, this->y - v.y, this->z - v.z);
		}
		
		/// Negate
		const Vector3D<T> operator- () const {
			return Vector3D<T> ( -this->x, -this->y, -this->z );
		}

		/// Dot product
		const float operator^ (const Vector3D<T> & v) const {
			return this->x * v.x + this->y * v.y + this->z * v.z;
		}
	
		/// Cross product
		const Vector3D<T> operator* (const Vector3D<T> & v) const {
			return Vector3D<T>(
				this->y * v.z - this->z * v.y,
				this->z * v.x - this->x * v.z,
				this->x * v.y - this->y * v.x
			);
		}
	
		/// Scale multiplying
		const Vector3D<T> operator* (const float s) const {
			return Vector3D<T> ( this->x * s, this->y * s, this->z * s );
		}
		
		/// Scale multiplying
		void operator*= (const float s) {
			this->x *= s; this->y *= s; this->z *= s;
		}
		
		/// Scale multiplying
		friend const Vector3D<T> operator* (const float s, const Vector3D<T> & v) {
			return Vector3D<T> ( v.x * s, v.y * s, v.z * s );
		}
		
		/// Scale dividing
		const Vector3D<T> operator/ (const float s) const {
			if (s == 0.f)
				throw Exceptions::DivisionByZeroException("Vector = Vector / 0 is not allowed");
			return Vector3D<T> ( this->x / s, this->y / s, this->z / s );
		}
		
		/// Scale dividing
		void operator/= (const float s) {
			if (s == 0.f)
				throw Exceptions::DivisionByZeroException("Vector /= 0 is not allowed");
			this->x /= s; this->y /= s; this->z /= s;
		}

};

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////

#else

namespace Types {
	template <typename T> class Vector3D;
}

#endif
